﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{
    public class Arc3d : Curve3d
    {
        public override Curve3dType Type => Curve3dType.Arc3d;
        [JsonInclude]
        public Vector3 Center { get; set; }
        public double Radius { get; set; }
        public double StartAngle { get; set; }
        public double EndAngle { get; set; }
        private Vector3 normal;
        public Vector3 Normal { get=> this.normal; set => computeStartAndEndPoint(value); }
        private Vector3 XAxis { get; set; } = new Vector3(1, 0, 0);
        public void UseFixedDiv(int div)
        {
            fixediv = div;
        }
        public bool IsClockwise { get; set; }

        public double ArcLength;
        private int fixediv = -1;

        public Arc3d()
        {

        }
        public Arc3d(Vector3 center, double radius, double startAngle, double endAngle, bool isClockwise = false)
        {
            Center = center;
            Radius=radius;
            StartAngle = startAngle;
            EndAngle = endAngle;
            IsClockwise = isClockwise;
            computeStartAndEndPoint(new Vector3(0,0,1));
        }
        /// <summary>
        /// 将角度标准化为0~2π之间
        /// </summary>
        /// <param name="angle"></param>
        /// <returns></returns>
        private double normalize(double angle)
        {
            while (angle < 0) angle += Utils.TwoPI;
            while (angle > Utils.TwoPI) angle -= Utils.TwoPI;
            return angle;
        }
        /// <summary>
        /// 计算弧起始结束点和弧长
        /// </summary>
        private void computeStartAndEndPoint(Vector3 normal)
        {
            this.normal = normal;
            Vector3 planeS = new Vector3(Radius * Math.Cos(StartAngle), Radius * Math.Sin(StartAngle));
            Vector3 planeE = new Vector3(Radius * Math.Cos(EndAngle), Radius * Math.Sin(EndAngle));

            var axis = new Vector3(0, 0, 1).Cross(this.Normal).Normalize();
            var angle = new Vector3(0, 0, 1).AngleTo(this.Normal);
            var mat1 = new Matrix4();
            mat1.MakeRotationAxis(axis, angle);
            var mat2 = new Matrix4();
            mat2.MakeTranslation(this.Center.X, this.Center.Y, this.Center.Z);
            this.Start = planeS.ApplyMatrix4(mat1).ApplyMatrix4(mat2);
            this.End = planeE.ApplyMatrix4(mat1).ApplyMatrix4(mat2);
            ArcLength = GetThetaLength() * Radius;
        }
        private double GetThetaLength()
        {
            return normalize(IsClockwise ? StartAngle - EndAngle : EndAngle - StartAngle);
        }
        public override List<Vector3> GetPoints(int div =32)
        {
            if (fixediv != -1)
            {
                div = fixediv;
            }
            var points = new List<Vector3>();
            Vector3 arcS = new Vector3(Radius * Math.Cos(StartAngle), Radius * Math.Sin(StartAngle));
            Vector3 arcE = new Vector3(Radius * Math.Cos(EndAngle), Radius * Math.Sin(EndAngle));
            points.Add(arcS);
            if (div <= 0)
            {
                div = 1;
            }
            var deltaAngle = GetThetaLength ()/ div;
            for (int i = 1; i < div ; i++)
            {
                var currPoint = arcS.Clone().RotateRoundAxis(new Vector3(),new Vector3(0,0,1), deltaAngle * i * (IsClockwise?-1:1));
                points.Add(currPoint);
            }
            points.Add(arcE);
            var axis = new Vector3(0, 0, 1).Cross(this.Normal).Normalize();
            var angle = new Vector3(0, 0, 1).AngleTo(this.Normal);
            var mat1 = new Matrix4();
            mat1.MakeRotationAxis(axis, angle);
            var mat2 = new Matrix4();
            mat2.MakeTranslation(this.Center.X, this.Center.Y, this.Center.Z);
            return points.Select(n => n.ApplyMatrix4(mat1).ApplyMatrix4(mat2)).ToList();
        }
        public override void Copy(Curve3d src)
        {
            var arc = src as Arc3d;
            this.Center = arc.Center.Clone();
            this.Radius = arc.Radius;
            this.StartAngle = arc.StartAngle;
            this.EndAngle = arc.EndAngle;
            this.IsClockwise = arc.IsClockwise;
            this.Normal = arc.Normal.Clone();
        }
        public override Curve3d Clone()
        {
            var newObj = new Arc3d();
            newObj.Copy(this);
            return newObj;
        }
        public override Curve3d Translate(Vector3 offset)
        {
            this.Center.Add(offset);
            computeStartAndEndPoint(this.normal);
            return this;
        }

        public override Curve3d RotateRoundAxis(Vector3 origin, Vector3 axis, double angle)
        {
            this.Center.RotateRoundAxis(origin, axis, angle);
            this.Normal.RotateRoundAxis(origin, axis, angle);
            return this;
        }

        public override Curve3d Reverse()
        {
            this.IsClockwise = !this.IsClockwise;
            var temp = this.StartAngle;
            this.StartAngle = this.EndAngle;
            this.EndAngle = temp;
            this.computeStartAndEndPoint(this.normal);
            return this;
        }
    }
}
